[AWS IoT Core] カスタム認証(ユーザー名・パスワード)を使用してMQTTでPublish/Subscribeしてみました
1 はじめに
IoT事業部の平内(SIN)です。
AWS IoT Coreへのアクセスでは、カスタム認証によって、独自の認証認可を構築することができます。
上のように、前回、HTTPSでの接続を確認しましたが、今回は、MQTTを使用して、カスタム認証(ユーザー名・パスワード)でメッセージのパブリッシュ及び、サブスクライブをしてみました。
IoT Coreでサポートされているプロトコルの一覧の中では、5番目のものとなります。一覧からも分かる通り、ポート8883では、利用できないので注意が必要です。
Protocols, authentication, and port mappings
No. | Protocol | Operations supported | Authentication | Port | ALPN protocol name |
---|---|---|---|---|---|
1 | MQTT over WebSocket | Publish, Subscribe | Signature Version 4 | 443 | N/A |
2 | MQTT over WebSocket | Publish, Subscribe | Custom authentication | 443 | N/A |
3 | MQTT | Publish, Subscribe | X.509 client certificate | 443 | x-amzn-mqtt-ca |
4 | MQTT | Publish, Subscribe | X.509 client certificate | 8883 | N/A |
5 | MQTT | Publish, Subscribe | Custom authentication | 443 | mqtt |
6 | HTTPS | Publish only | Signature Version 4 | 443 | N/A |
7 | HTTPS | Publish only | X.509 client certificate | 443 | x-amzn-http-ca |
8 | HTTPS | Publish only | X.509 client certificate | 8443 | N/A |
9 | HTTPS | Publish only | Custom authentication | 443 | N/A |
参考:How to Use Your Own Identity and Access Management Systems to Control Access to AWS IoT Resources
2 Lambda
MQTTでアクセスした場合、下記のようなリクエストがLambdaに到着します。
{ "protocolData": { "tls": { "serverName": "xxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com" }, "mqtt": { "username": "user01?x-amz-customauthorizer-name=custom_auth_mqtt_2021_08_21", "password": "cGFzczAx", "clientId": "client_id" } }, "protocols": [ "tls", "mqtt" ], "signatureVerified": False, "connectionMetadata": { "id": "54300d4a-d787-bb33-bbab-e61eb9f07893" } }
そして、Lambdaから返すのは、以下の内容になります。
- IsAuthenticated: 認証の有効無効を返すブール値
- PrincipalId: 識別子(1〜128文字)
- PolicyDocuments: JSON形式のポリシー(各ポリシーは、2,048文字以内で、10個まで指定可能)
- DisconnectAfterInSeconds: AWS IoT ゲートウェイへの接続期間(300〜86,400秒)
- RefreshAfterInSeconds: ポリシーをキャッシュする期間(300〜86,400秒)
オーソライザーで「署名を有効」としていない場合、signatureVerifiedは、Falseとなっています。
この場合、Lambdaがコールされた時点で、認証は、完了していないので、送られてきた、ユーザー名、パスワード、クライアントIDなどを使用して認証するロジックをLambda内に記述する事になります。
下記のコードでは、ユーザー名が、user01で、パスワードがpass01だった場合、トピック(my-topic)へのPub/Subを許可しています。また、返されるポリシーは、client_idからのConnectのみ許可するものですので、クライアントIDもclient_idであることが必要です。
custom_auth_mqtt_2021_08_21
import json import base64 policy = { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "iot:Connect", "Resource": "arn:aws:iot:ap-northeast-1:*:client/client_id" }, { "Effect": "Allow", "Action": [ "iot:Publish", "iot:Receive", ], "Resource": "arn:aws:iot:ap-northeast-1:*:topic/my-topic" }, { "Effect": "Allow", "Action": "iot:Subscribe", "Resource": "arn:aws:iot:ap-northeast-1:*:topicfilter/my-topic" } ] } def lambda_handler(event, context): print(event) is_authenticated = False username = "" password = "" if "protocolData" in event: protocolData = event["protocolData"] if "mqtt" in protocolData: mqtt = protocolData["mqtt"] if "username" in mqtt: tmp = mqtt["username"].split("?") username = tmp[0] if "password" in mqtt: password = mqtt["password"] password = base64.b64decode(password).decode() #Base64デコード if(username == "user01" and password == "pass01"): is_authenticated = True principal_id = "CustomAuthId" disconnect = 84000 refresh = 300 return { "isAuthenticated": is_authenticated, "principalId": principal_id, "disconnectAfterInSeconds": disconnect, "refreshAfterInSeconds": refresh, "policyDocuments": [ policy ] }
3 オーソライザー
IoT Coreのコンソールから安全性 - オーソライザーと辿り、新規にオーソライザーを作成します。 設定した内容は、以下の通りです
- オーソライザーの名前: custom_auth_mqtt_2021_08_21としました
- オーソライザー関数: custom_auth_mqtt_2021_08_21 上記で作成したもの
- トークン署名を有効化: チェックなし
- オーソライザーアプティブ: チェック
トークン署名の有効化については、後て変更はできず、もし、変えたい場合は、オーソライザー自体を改めて作成する必要があります。
4 動作確認
AWS IoT Device SDK v2 for Pythonで作成したクライアントのコードです。
接続後、トピック(my-topic)をSubscribeし、同トピックに3回メッセージをPublishして終了します。
from awscrt import io, mqtt import json import time endpoint = "xxxxxxxxxxxx.iot.ap-northeast-1.amazonaws.com" root_ca = "./certs/root-CA.crt" authorizer_name = "custom_auth_mqtt_2021_08_21" topic = "my-topic" client_id = "client_id" username = "user01" password = "pass01" def on_message_received(topic, payload, **kwargs): print("received: {}".format(payload.decode())) if __name__ == '__main__': event_loop_group = io.EventLoopGroup(1) host_resolver = io.DefaultHostResolver(event_loop_group) client_bootstrap = io.ClientBootstrap(event_loop_group, host_resolver) tls_options = io.TlsContextOptions() tls_options.alpn_list = ['mqtt'] tls_options.override_default_trust_store_from_path(ca_dirpath=None, ca_filepath=root_ca) tls_ctx = io.ClientTlsContext(options=tls_options) client = mqtt.Client(client_bootstrap, tls_ctx) username += "?x-amz-customauthorizer-name={}".format(authorizer_name) mqtt_connection = mqtt.Connection(client=client, host_name=endpoint, port=443, client_id=client_id, clean_session=True, keep_alive_secs=6, username=username, password=password) connect_future = mqtt_connection.connect() connect_future.result() print("Connected!") # Subscribe subscribe_future, packet_id = mqtt_connection.subscribe( topic=topic, qos=mqtt.QoS.AT_LEAST_ONCE, callback=on_message_received) subscribe_future.result() for i in range(3): # Publish payload = { "counter": i } mqtt_connection.publish( topic=topic, payload=json.dumps(payload), qos=mqtt.QoS.AT_LEAST_ONCE) time.sleep(1) disconnect_future = mqtt_connection.disconnect() disconnect_future.result()
自分の送信したメッセージを受信できている様子です。
テストクライアントでも確認できています。
5 最後に
今回は、MQTTを使用して、ユーザー名とパスワードでメッセージブローカーへの接続を試してみました。
署名を使用しない場合、パスワード等だけの認証が可能にはなりますが、Lambdaのトリガーに制限が効かなくなるため、使用方法はよく検討する必要があるでしょう。
https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/custom-authorizer.html
MQTTでも、署名をユーザーフィールドで設定できるようにドキュメントには記載されていましたが、すいません、ちょっと手元ではうまくオーソライザーに届きませんでした。何かコツなどあれば、是非教えてやってください。
https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/custom-auth.html
username?x-amz-customauthorizer-name=${name} &x-amz-customauthorizer-signature=${sign}&token-name=${token-value}
6 参考リンク
AWS IoT Enhanced Custom Authorizer Demo
Connecting to AWS IoT Core by using custom authentication
[AWS IoT Core] カスタム認証を使用してHTTPSでPublishしてみました
[AWS IoT Core] カスタム認証(ユーザー名・パスワード)を使用してMQTTでPublish/Subscribeしてみました
[AWS IoT Core] カスタム認証を使用してMQTT over WebSocketでPublish/Subscribeしてみました